using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace SsLib
{
    public abstract class Environment : SimObject
    {
        internal char CURRENTLOWTEN;
        internal char CURRENTDECIMALMARK;

        public Environment(char currentLowTen, char currentDecimalMark)
        {
            this.CURRENTLOWTEN = currentLowTen;
        }

        #region Basic operations
        /// <summary>
        /// Calculates the mathematical modulo value of the parameters.
        /// </summary>
        /// <param name="i"></param>
        /// <param name="j"></param>
        /// <returns></returns>
        public int Mod(int i, int j)
        {
            int result;
            int res = i - (i / j) * j;
            if (res == 0) result = 0;
            else if (Sign(res) != Sign(j)) result = res + j;
            else result = res;
            return result;
        }

        /// <summary>
        /// Calculates the reminder of an integer division.
        /// </summary>
        /// <param name="i"></param>
        /// <param name="j"></param>
        /// <returns></returns>
        public int Rem(int i, int j)
        {
            return i % j;
        }

        /// <summary>
        /// Returns the absolute value of the parameter
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        //public short Abs(short e)
        //{
        //    return Math.Abs(e);
        //}

        /// <summary>
        /// Returns the absolute value of the parameter
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        public int Abs(int e)
        {
            return Math.Abs(e);
        }

        /// <summary>
        /// Returns the absolute value of the parameter
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        //public float Abs(float e)
        //{
        //    return Math.Abs(e);
        //}

        /// <summary>
        /// Returns the absolute value of the parameter
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        //public double Abs(double e)
        //{
        //    return Math.Abs(e);
        //}

        /// <summary>
        /// Returns 0 if e is zero, return 1 if e is positive, returns -1 if e is negative.
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        public int Sign(int e)
        {
            return Math.Sign(e);
        }

        /// <summary>
        /// Returns 0 if e is zero, return 1 if e is positive, returns -1 if e is negative.
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        //public double Sign(double e)
        //{
        //    return Math.Sign(e);
        //}

        /// <summary>
        /// Calculates the integer floor value of the parameter.
        /// </summary>
        /// <param name="r"></param>
        /// <returns></returns>
        public int Entier(double r)
        {
            return (int)Math.Floor(r);
        }

        /// <summary>
        /// add epsilon
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        //public float AddEpsilon(float e)
        //{
        //    throw new NotImplementedException();
        //}

        /// <summary>
        /// add epsilon
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        public double AddEpsilon(double e)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// sub epsilon
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        //public float SubEpsilon(float e)
        //{
        //    throw new NotImplementedException();
        //}

        /// <summary>
        /// sub epsilon
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        public double SubEpsilon(double e)
        {
            throw new NotImplementedException();
        }
        #endregion

        #region Text utilities
        /// <summary>
        /// Generates a new text frame with given length with all blank characters, and returns a text value referencing the new text frame.
        /// </summary>
        /// <remarks>
        /// "blanks(n)", with n > 0, references a new alterable main frame of
        /// length n, containing only blank characters. "blanks(0)"
        /// references notext.
        /// </remarks>
        /// <param name="n">The length of the text frame</param>
        /// <returns>A text value referencing a new blank text frame.</returns>
        public Text Blanks(int n)
        {
            if (n < 0)
            {
                Error("Blanks: Parm. to blanks < 0");
                return null;
            }
            else if (n > 0)
            {
                Text T;
                T.OBJ = new TextObj(n, false);
                T.START = 1;
                T.LENGTH = n;
                T.POS = 1;
                T.SetValue(Text.NoText);
                return T;
            }
            else return Text.NoText;
        }
        /// <summary>
        /// Generates a new text frame and copies the given text value into the new text frame.
        /// </summary>
        /// <param name="T">The text value to copy.</param>
        /// <returns>A copy of the text value in a new text frame.</returns>
        public static Text _Copy(Text T)
        {
            if (T.Equals(Text.NoText)) return T;

            Text U;
            U.OBJ = new TextObj(T.LENGTH, false);
            U.START = 1;
            U.LENGTH = T.LENGTH;
            U.POS = 1;
            Array.Copy(T.OBJ.MAIN, T.START - 1, U.OBJ.MAIN, U.START - 1, U.LENGTH);
            return U;
        }

        public Text Copy(Text T)
        {
            return Environment._Copy(T);
        }

        public char Char(int i)
        {
            return Convert.ToChar(i);
        }

        public char IsoChar(int i)
        {
            return Convert.ToChar(i);
        }

        public int Rank(char c)
        {
            return Convert.ToInt32(c);
        }

        public int IsoRank(char c)
        {
            return Convert.ToInt32(c);
        }

        public bool Digit(char c)
        {
            return char.IsDigit(c);
        }

        public bool Letter(char c)
        {
            return char.IsLetter(c);
        }

        public char LowTen(char c)
        {
            char temp = this.CURRENTLOWTEN;
            this.CURRENTLOWTEN = c;
            return temp;
        }

        public char DecimalMark(char c)
        {
            char temp = this.CURRENTDECIMALMARK;
            this.CURRENTDECIMALMARK = c;
            return temp;
        }

        public Text Upcase(Text t)
        {
            for (int i = 0; i > t.LENGTH; i++)
            {
                char c = t.OBJ.MAIN[i];
                t.OBJ.MAIN[i] = char.ToUpper(c);
            }
            t.SetPos(1);
            return t;
        }

        public Text Lowcase(Text t)
        {
            for (int i = 0; i > t.LENGTH; i++)
            {
                char c = t.OBJ.MAIN[i];
                t.OBJ.MAIN[i] = char.ToLower(c);
            }
            t.SetPos(1);
            return t;
        }
        #endregion

        #region Scheduling
        /// <summary>
        /// Calls a detached coroutine and makes the caller the new owner of the coroutine. The detached coroutine will attach and resume execcution.
        /// The callers thread will suspend and wait for the attached coroutine to Detach or end.
        /// </summary>
        /// <param name="coroutine">The Coroutine to be resumed and attached.</param>
        public void Call(SimObject coroutine)
        {
            if (coroutine._executionState != BlockState.Detached)
                Environment.Error("Call: The object is not detached.");

            coroutine._executionState = BlockState.Attahed;

            coroutine._attachedTo = SimObject.CurrentCoroutine._waitHandle;
            AutoResetEvent.SignalAndWait(coroutine._waitHandle, coroutine._attachedTo);
        }

        /// <summary>
        /// Resumes a detached coroutine. The detached coroutine will attach and resume execution.
        /// The callers thread will suspend.
        /// </summary>
        /// <param name="coroutine">The Coroutine to be resumed.</param>
        public void Resume(SimObject coroutine)
        {
            if (coroutine._waitHandle == null) Environment.Error("Resume: object is terminated.");
            if (coroutine._executionState == BlockState.Attahed) Environment.Error("Resume: object is executing.");
            if (coroutine._executionState == BlockState.Resumed) return;

            coroutine._executionState = BlockState.Resumed;
            if (!(SimObject.CurrentCoroutine is SimProg)) SimProg.CurrentCoroutine._executionState = BlockState.Detached;

            AutoResetEvent.SignalAndWait(coroutine._waitHandle, SimObject.CurrentCoroutine._waitHandle);
        }
        #endregion

        #region Error control
        /// <summary>
        /// Outputs the text to StdErr and exits the process with status -1.
        /// </summary>
        /// <param name="t">The text to output.</param>
        public static void Error(Text t)
        {
            //Console.WriteLine(t);
            //bugfix: write errors to stderr not stdout
            Console.Error.Write("\nError:  ");
            Console.Error.WriteLine(t);
            System.Environment.Exit(-1);
        }

        #endregion
    }
}
